# -*- coding: utf-8 -*-

from hlm.node import *

from ip.fabrics import ChannelLoadFabric


class NodeRouterLoad(Node):
    def initialize(self, params: ObjectDict) -> None:
        params.multiplexer: int
        params.routing_map: 'List.List.int'
        # Save the bandwidth multiplexer.
        self.const.multiplexer = params.multiplexer
        self.port.receiver = self.port_channel_receiver(ChannelLoadFabric)
        self.port.receiver_lane = self.port.receiver.Bus
        # Save the routing map by calculate the mask ids.
        self.reg.mask_map = [0 for _ in params.routing_map]
        for ii in range(len(params.routing_map)):
            self.reg.mask = 0
            for routing_id in params.routing_map[ii]:
                self.reg.mask |= (1 << routing_id)
            # Save the mask
            self.reg.mask_map[ii] = self.reg.mask
        # Create multiple instances sending ports.
        self.port.senders = [self.port_channel_sender(ChannelLoadFabric)
                             for _ in params.routing_map]
        self.port.sender_lanes = [self.port.senders[ii].Bus
                                  for ii in range(len(params.routing_map))]
        # Create status registers.
        self.reg.is_routing = False
        self.reg.dispatch = [False for _ in self.reg.mask_map]

    tasks = ['task_routing']

    def task_routing(self):
        for _ in range(self.const.multiplexer):
            # Check routing states.
            if not self.reg.is_routing and \
                    not self.port.receiver_lane.is_any_empty():
                # We got something need to be dispatch, read the routing attribute.
                self.reg.in_pid, self.reg.in_addr, self.reg.in_data, \
                self.reg.in_sb_addr, self.reg.in_ain_addr, self.reg.in_clr = \
                    self.port.receiver.Bus.get()
                # Set routing state.
                self.reg.is_routing = True
                # Calculate the routing targets.
                for ii in range(len(self.reg.mask_map)):
                    self.reg.dispatch[ii] = \
                        (self.reg.in_pid & self.reg.mask_map[ii]) > 0
            # Check the routing state.
            if not self.reg.is_routing:
                continue
            # Reset the routing completed checker.
            self.reg.dispatch_in_progress = False
            # Find whether all the target has been dispatched.
            for ii in range(len(self.reg.dispatch)):
                # Check availability.
                if self.reg.dispatch[ii] and not self.port.sender_lanes[ii].is_any_full():
                    # Put the data to target port.
                    self.port.sender_lanes[ii].put(pid=self.reg.in_pid,
                                                   addr=self.reg.in_addr,
                                                   data=self.reg.in_data,
                                                   sb_addr=self.reg.in_sb_addr,
                                                   ain_addr=self.reg.in_ain_addr,
                                                   clr=self.reg.in_clr)
                    # Reset the dispatch flag.
                    self.reg.dispatch[ii] = False
                    # Update the in progress result.
                    self.reg.dispatch_in_progress = self.reg.dispatch_in_progress or self.reg.dispatch[ii]
            # Check whether we complete the dispatch.
            if not self.reg.dispatch_in_progress:
                self.reg.is_routing = False
